/*
 * StringTraits.h
 *
 *  Created on: Jan 25, 2013
 *      Author: Lucifer
 */

#ifndef __BFX_STRINGTRAITS_H__
#define __BFX_STRINGTRAITS_H__

#include <stdlib.h>
#include <ctype.h>
#include <stddef.h>
#include <wchar.h>
#include <wctype.h>
#include <stdarg.h>

#ifndef	_WIN32
/*
 * String helpers
 */
extern "C" char * __cdecl strupr(char * string);
extern "C" char * __cdecl strlwr(char * string);
extern "C" char * __cdecl strrev(char * string);
extern "C" wchar_t * __cdecl wcsrev(wchar_t * string);
extern "C" wchar_t * __cdecl wcslwr(wchar_t * wsrc);
extern "C" wchar_t * __cdecl wcsupr(wchar_t * wsrc);

#endif

namespace BFX
{

//////////////////////////////////////////////////////////////////////////////

template<typename T>
class BFX_TMPL StringTraitsBase
{
	typedef T XCHAR;
	typedef T* PXSTR;
	typedef const T* PCXSTR;

public:
	static int __cdecl StringLength(const XCHAR* psz) throw ()
	{
		const XCHAR* pchEOS = psz;
		while (*pchEOS++)
			;
		return (pchEOS - psz - 1);
	}
	static void __cdecl FillChars(XCHAR ch, int nLength, XCHAR* pch) throw ()
	{
		for (int i = 0; i < nLength; i++)
		{
			pch[i] = ch;
		}
	}
	static int __cdecl StringCompare(const XCHAR* pszA, const XCHAR* pszB) throw ()
	{
		int nRet = 0;
		if (pszA == NULL || pszB == NULL)
			return 0;
		while (!(nRet = (int) (*pszA - *pszB)) && *pszB)
			++pszA, ++pszB;

		if (nRet < 0)
			nRet = -1;
		else if (nRet > 0)
			nRet = 1;
		return (nRet);
	}
	static int __cdecl StringCompareIgnore(const XCHAR* pszA, const XCHAR* pszB) throw ()
	{
		int chA, chB;
		if (pszA == NULL || pszB == NULL)
			return 0;
		do
		{
			chA = toupper((int) *pszA);
			chB = toupper((int) *pszB);
			pszA++;
			pszB++;
		} while ((chA) && (chA == chB));
		return (int) (chA - chB);
	}
	static const XCHAR* __cdecl StringFindString(const XCHAR* pszBlock,
			const XCHAR* pszMatch) throw ()
	{
		XCHAR *pchCur = (XCHAR *) pszBlock;
		XCHAR *s1, *s2;
		if (!*pszMatch)
			return pszBlock;

		while (*pchCur)
		{
			s1 = pchCur;
			s2 = (XCHAR *) pszMatch;
			while (*s1 && *s2 && !(*s1 - *s2))
				s1++, s2++;
			if (!*s2)
				return (pchCur);
			pchCur++;
		}

		return (NULL);
	}
	static const XCHAR* __cdecl StringFindStringRev(const XCHAR* pszBlock, const XCHAR* pszMatch)
	{
		// This function finds the last occurrence of the substring in the string.
		// The terminating NULL characters are not compared.
		const XCHAR *pch = NULL;
		while (1)
		{
			const XCHAR *p = StringFindString(pszBlock, pszMatch);
			if (!p)
				break;
			pch = p;
			pszBlock = p + 1;
		}
		return pch;
	}
	static const XCHAR* __cdecl StringFindChar(const XCHAR* pszBlock, XCHAR chMatch) throw ()
	{
		while (*pszBlock && *pszBlock != chMatch)
			pszBlock++;

		return (*pszBlock == chMatch) ? pszBlock : NULL;
	}
	static const XCHAR* __cdecl StringFindCharRev(const XCHAR* psz, XCHAR ch) throw ()
	{
		const XCHAR* pchStart = psz;

		// find end of string
		while (*psz++)
			;
		// search towards front
		while (--psz != pchStart && *psz != ch)
			;

		// char found ??
		return (*psz == ch) ? psz : NULL;
	}

	static const XCHAR* __cdecl StringScanSet(const XCHAR* pszBlock, const XCHAR* pszMatch) throw()
	{
		// 1st char in control string stops search
		while (*pszBlock)
		{
			for (XCHAR* pch = (XCHAR*) pszMatch; *pch; pch++)
			{
				if (*pch == *pszBlock)
					return (pszBlock);
			}
			pszBlock++;
		}
		return (NULL);
	}
};

//////////////////////////////////////////////////////////////////////////////
#ifdef	_MSC_VER
#pragma warning (disable : 4996)
#endif	//	_MSC_VER

template<typename T = char>
class BFX_TMPL StringTraitsT: public StringTraitsBase<T>
{
public:
	static int __cdecl GetFormattedLength(const char* pszFormat, va_list args) throw ()
	{
		return vsnprintf(NULL, 0, pszFormat, args);
	}
	static int __cdecl Format(char* pszBuffer, size_t nLength, const char* pszFormat,
			va_list args) throw ()
	{
		return vsnprintf(pszBuffer, nLength, pszFormat, args);
	}
	static bool IsSpace(char ch) throw()
	{
		return (isspace(ch) != 0);
	}
	static char* __cdecl StringUppercase(char* psz) throw()
	{
		return strupr(psz);
	}
	static char* __cdecl StringLowercase(char* psz) throw()
	{
		return strlwr(psz);
	}
	static char* __cdecl StringReverse(char* psz) throw()
	{
		return strrev(psz);
	}
};

//////////////////////////////////////////////////////////////////////////////

template<>
class BFX_TMPL StringTraitsT<wchar_t> : public StringTraitsBase<wchar_t>
{
public:
	static int __cdecl GetFormattedLength(const wchar_t* pszFormat, va_list args) throw ()
	{
		return vswprintf(NULL, 0, pszFormat, args);
	}
	static int __cdecl Format(wchar_t* pszBuffer, size_t nLength, const wchar_t* pszFormat,
			va_list args) throw ()
	{
		return vswprintf(pszBuffer, nLength, pszFormat, args);
	}
	static bool IsSpace(wchar_t ch) throw()
	{
		return (iswspace(ch) != 0);
	}
	static wchar_t* __cdecl StringUppercase(wchar_t* psz) throw()
	{
		return wcsupr(psz);
	}
	static wchar_t* __cdecl StringLowercase(wchar_t* psz) throw()
	{
		return wcslwr(psz);
	}
	static wchar_t* __cdecl StringReverse(wchar_t* psz) throw()
	{
		return wcsrev(psz);
	}
};

} /* namespace BFX */

#endif /* __BFX_STRINGTRAITS_H__ */
